﻿//==============================================================================
// File: SerializableDictionary.cs
// Created: 2010-07-12
// Author: Piotr Włodek
//==============================================================================
// This file is a part of MAMMOTH project.
// Copyright (C) 2010 AGH University of Science and Technology, Krakow.
// https://caribou.iisg.agh.edu.pl/trac/mammoth
//
// Mammoth is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// 
// Mammoth is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Lesser General Public License for more details.
// 
// You should have received a copy of the GNU Lesser General Public License
// along with WpfRcp. If not, see http://www.gnu.org/licenses/.
//==============================================================================

using System;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.Serialization;

namespace WpfRcp.PropertyModel.Collections
{
    /// <summary>
    /// Represents a serializable dictionary.
    /// </summary>
    /// <typeparam name="TKey">Key type.</typeparam>
    /// <typeparam name="TValue">Value type.</typeparam>
    /// <remarks>Both key and value have to be serializable.</remarks>
    [Serializable]
    public class SerializableDictionary<TKey,TValue> : IDictionary<TKey,TValue>
    {
        private readonly Type m_DictionaryType;

        [NonSerialized]
        private IDictionary<TKey, TValue> m_Dictionary;

        private KeyValuePair<TKey, TValue>[] m_DictionaryValues;

        /// <summary>
        /// Initializes a new instance of the <see cref="SerializableDictionary{TKey,TValue}"/> class.
        /// </summary>
        /// <param name="dictionaryType">Type of the underlying dictionary to use.</param>
        public SerializableDictionary(Type dictionaryType)
        {
            if (dictionaryType == null) throw new ArgumentNullException("dictionaryType");

            m_DictionaryType = dictionaryType;
            m_Dictionary = (IDictionary<TKey, TValue>) Activator.CreateInstance(dictionaryType);
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="SerializableDictionary{TKey,TValue}"/> class.
        /// </summary>
        public SerializableDictionary() : this(typeof(Dictionary<TKey,TValue>))
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="SerializableDictionary{TKey,TValue}"/> class.
        /// </summary>
        /// <param name="dictionary">Original dictionary.</param>
        public SerializableDictionary(IDictionary<TKey,TValue> dictionary)
        {
            if (dictionary == null) throw new ArgumentNullException("dictionary");

            m_DictionaryType = dictionary.GetType();
            m_Dictionary = dictionary;
        }

        #region Private Serialization

        [OnSerializing]
        private void OnSerializing(StreamingContext context)
        {
            m_DictionaryValues = new KeyValuePair<TKey, TValue>[m_Dictionary.Count];

            int i = 0;
            foreach (var value in m_Dictionary)
            {
                m_DictionaryValues[i++] = value;
            }
        }

        [OnDeserialized]
        private void OnDeserialized(StreamingContext context)
        {
            m_Dictionary = (IDictionary<TKey, TValue>)Activator.CreateInstance(m_DictionaryType);
            foreach (var keyValuePair in m_DictionaryValues)
            {
                m_Dictionary.Add(keyValuePair.Key, keyValuePair.Value);
            }
            m_DictionaryValues = null;
        }

        #endregion

        #region Implementation of IEnumerable

        public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
        {
            return m_Dictionary.GetEnumerator();
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }

        #endregion

        #region Implementation of ICollection<KeyValuePair<TKey,TValue>>

        public void Add(KeyValuePair<TKey, TValue> item)
        {
            m_Dictionary.Add(item);
        }

        public void Clear()
        {
            m_Dictionary.Clear();
        }

        public bool Contains(KeyValuePair<TKey, TValue> item)
        {
            return m_Dictionary.Contains(item);
        }

        public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)
        {
            m_Dictionary.CopyTo(array, arrayIndex);
        }

        public bool Remove(KeyValuePair<TKey, TValue> item)
        {
            return m_Dictionary.Remove(item);
        }

        public int Count
        {
            get { return m_Dictionary.Count; }
        }

        public bool IsReadOnly
        {
            get { return m_Dictionary.IsReadOnly; }
        }

        #endregion

        #region Implementation of IDictionary<TKey,TValue>

        public bool ContainsKey(TKey key)
        {
            return m_Dictionary.ContainsKey(key);
        }

        public void Add(TKey key, TValue value)
        {
            m_Dictionary.Add(key, value);
        }

        public bool Remove(TKey key)
        {
            return m_Dictionary.Remove(key);
        }

        public bool TryGetValue(TKey key, out TValue value)
        {
            return m_Dictionary.TryGetValue(key, out value);
        }

        public TValue this[TKey key]
        {
            get { return m_Dictionary[key]; }
            set { m_Dictionary[key] = value; }
        }

        public ICollection<TKey> Keys
        {
            get { return m_Dictionary.Keys; }
        }

        public ICollection<TValue> Values
        {
            get { return m_Dictionary.Values; }
        }

        #endregion
    }
}